#pragma once

#include <string>

// #define MySelf

const std::string protocal_sep = "\n";

std::string Encode(std::string& text)
{
    std::string package = std::to_string(text.size());
    package += protocal_sep;
    package += text;
    package += protocal_sep;
    return package;
}
bool Decode(std::string& package, std::string& text)
{
    size_t pos = package.find(protocal_sep);
    if (pos == std::string::npos)
        return false;
    size_t len = std::stoi(package.substr(0, pos));
    size_t total_len = len + pos + 2;
    if (total_len > package.size())
        return false;
    text = package.substr(pos + 1, len);
    package.erase(0, total_len);
    return true;
}

#ifdef MySelf
const std::string blank_space_sep = " ";
#else
#include <jsoncpp/json/json.h>
#endif

class Request
{
public:
    Request(int left = 0, int right = 0, char oper = 0)
        : _left(left)
        , _right(right)
        , _oper(oper)
    {}

    // struct Request --> std::string
    bool Serialize(std::string& right)
    {
#ifdef MySelf
        // left' 'oper' 'right
        std::string text = std::to_string(_left);
        text += blank_space_sep;
        text += _oper;
        text += blank_space_sep;
        text += std::to_string(_right);
        // len'\n'left' 'oper' 'right'\n'
#else
        Json::Value root;
        root["left"] = _left;
        root["right"] = _right;
        root["oper"] = _oper;
        Json::FastWriter w;
        std::string text = w.write(root);
#endif
        right = Encode(text);
        return true;
    }
    // std::string --> struct Request
    bool Deserialize(std::string& package)
    {
        std::string text;
        if (Decode(package, text) == false)
            return false;
#ifdef MySelf
        size_t left = text.find(blank_space_sep);
        if (left == std::string::npos)
            return false;
        size_t right = text.rfind(blank_space_sep);
        if (right == std::string::npos)
            return false;
        if (left + 1 != right - 1)
            return false;
        _left = std::stoi(text.substr(0, left));
        _right = std::stoi(text.substr(right + 1));
        _oper = text[left + 1];
        return true;
#else
        Json::Value root;
        Json::Reader r;
        r.parse(text, root);
        _left = root["left"].asInt();
        _right = root["right"].asInt();
        _oper = root["oper"].asInt();
        return true;
#endif
    }

public:
    int _left;
    int _right;
    int _oper;
};

class Response
{
public:
    enum Response_Exitcode
    {
        RESPONSE_SUCCESS = 0,
        RESPONSE_DIV_ZERO,
        RESPONSE_UNKONW_OPER
    };

    Response(int result = 0, int exitcode = 0)
        : _result(result)
        , _exitcode(exitcode)
    {}

    // struct Request --> std::string
    bool Serialize(std::string& right)
    {
#ifdef MySelf
        // reuslt' 'exitcode
        std::string text = std::to_string(_result);
        text += blank_space_sep;
        text += std::to_string(_exitcode);
        // len'\n'reuslt' 'exitcode'\n'
#else      
        Json::Value root;
        root["result"] = _result;
        root["exitcode"] = _exitcode;
        Json::FastWriter w;
        std::string text = w.write(root);
#endif
        right = Encode(text);
        return true;
    }
    // std::string --> struct Request
    bool Deserialize(std::string& package)
    {
        std::string text;
        if (Decode(package, text) == false)
            return false;
#ifdef MySelf
        size_t left = text.find(blank_space_sep);
        if (left == std::string::npos)
            return false;
        size_t right = text.rfind(blank_space_sep);
        if (right == std::string::npos)
            return false;
        if (left != right)
            return false;
        _result = std::stoi(text.substr(0, left));
        _exitcode = std::stoi(text.substr(right + 1));
        return true;
#else
        Json::Value root;
        Json::Reader r;
        r.parse(text, root);
        _result = root["result"].asInt();
        _exitcode = root["exitcode"].asInt();
        return true;
#endif
    }
public:
    int _result;
    int _exitcode;
};